package com.gitee.wsl.data.input.ext

import com.gitee.wsl.data.input.SideJobScope
import com.gitee.wsl.data.input.InputHandlerScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.merge
import kotlinx.coroutines.flow.onEach


/**
 * Observe a Flow of Inputs that will run asynchronously in its own [SideJobScope]. This will
 * allow other Inputs to be processed and not be blocked by this Flow's subscription.
 *
 * The Flow subscription will remain active during the whole life of the associated ViewModel
 * and will be cancelled when the ViewModel is destroyed, when the side-job is restarted, or
 * it may terminate itself before then when the Flow completes.
 *
 * The side-job started from the Flow uses the resulting Input's simple class name as
 * its key.
 */
suspend inline fun <reified Inputs : Any, Events : Any, State : Any> InputHandlerScope<Inputs, Events, State>.observeFlows(
    key: String,
    vararg inputs: Flow<Inputs>,
) {
    sideJob(
        key = key,
    ) {
        merge(*inputs)
            .onEach { postInput(it) }
            .launchIn(this)
    }
}


/**
 * Observe a Flow of Inputs that will run asynchronously in its own [SideJobScope]. This will
 * allow other Inputs to be processed and not be blocked by this Flow's subscription.
 *
 * The Flow subscription will remain active during the whole life of the associated ViewModel
 * and will be cancelled when the ViewModel is destroyed, when the side-job is restarted, or
 * it may terminate itself before then when the Flow completes.
 *
 * The side-job started from the Flow uses the resulting Input's simple class name as
 * its key.
 */
suspend inline fun <reified Inputs : Any, Events : Any, State : Any> InputHandlerScope<Inputs, Events, State>.observeFlows(
    key: String,
    crossinline getInputs: SideJobScope<Inputs, Events, State>.() -> List<Flow<Inputs>>,
) {
    sideJob(
        key = key,
    ) {
        getInputs().merge()
            .onEach { postInput(it) }
            .launchIn(this)
    }
}